package com.yifeng.repo.tookit.oss.model;

import com.yifeng.repo.tookit.oss.model.Signature;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.LinkedHashMap;
import java.util.Map;

public class OssFormUploader {

    private static final String MODE_OBS = "obs";

    public String uploadStream(Signature sign, InputStream inputStream, String contentType, boolean asAttachment) {
        Map<String, String> fields = new LinkedHashMap<>();
        fields.put("key", sign.getKey());
        // 设置policy
        fields.put("policy", sign.getPolicy());

        // 区分obs和oss，上传文件所需的字段信息不一致
        if (MODE_OBS.equals(sign.getMode())){
            fields.put("content-type", sign.getContentType());
            fields.put("x-obs-acl", sign.getxObsAcl());
            fields.put("AccessKeyId", sign.getAccessId());
            fields.put("signature", sign.getSignature());
        } else {
            // 设置OSSAccessKeyId
            fields.put("OSSAccessKeyId", sign.getAccessId());
            // 生成签名
            fields.put("Signature", sign.getSignature());
        }

        try {
            this.uploadStreamByForm(sign.getHost(), fields, inputStream, contentType, asAttachment);
        } catch (Exception e) {
            throw new RuntimeException("OSS upload error!", e);
        }
        return sign.getUrl();
    }

    private void uploadStreamByForm(String urls, Map<String, String> fields, InputStream inputStream, String contentType, boolean asAttachment) throws Exception {
        HttpURLConnection conn = getHttpConnection(urls);
        String boundary = "19491001005";

        try {
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);

            DataOutputStream out = new DataOutputStream(conn.getOutputStream());
            // 遍历读取表单Map中的数据，将数据写入到输出流中。
            StringBuilder buf = new StringBuilder();
            for (Map.Entry<String, String> entry : fields.entrySet()) {
                String inputName = entry.getKey();
                String inputValue = entry.getValue();
                if (inputValue == null) {
                    continue;
                }

                if (buf.length() == 0) {
                    buf.append("--").append(boundary).append("\r\n");
                } else {
                    buf.append("\r\n").append("--").append(boundary).append("\r\n");
                }
                buf.append("Content-Disposition: form-data; name=\"").append(inputName).append("\"\r\n\r\n");
                buf.append(inputValue);
            }
            out.write(buf.toString().getBytes());

            // 读取文件信息，将要上传的文件写入到输出流中。
            StringBuilder buf2 = new StringBuilder();
            buf2.append("\r\n").append("--").append(boundary).append("\r\n");
            buf2.append("Content-Disposition: form-data; name=\"file\"; " + "filename=\"").append(inputStream.toString()).append("\"\r\n");
            if (asAttachment) {
                buf2.append("Content-Type: application/octet-stream").append("\r\n\r\n");
            } else {
                buf2.append("Content-Type: ").append(contentType).append("\r\n\r\n");
            }
            out.write(buf2.toString().getBytes());

            try (DataInputStream in = new DataInputStream(inputStream)) {
                this.copyStream(in, out, boundary);
            }

            String result = this.readExceptionResponse(conn);
            if (result != null) {
                throw new RuntimeException(result);
            }
        } finally {
            conn.disconnect();
        }
    }

    private void copyStream(DataInputStream in, DataOutputStream out, String boundary) throws IOException {
        int bytes;
        byte[] buff = new byte[64 * 1024];
        while ((bytes = in.read(buff)) != -1) {
            out.write(buff, 0, bytes);
        }
        in.close();

        byte[] ends = ("\r\n--" + boundary + "--\r\n").getBytes();
        out.write(ends);
        out.flush();
    }

    private String readExceptionResponse(HttpURLConnection conn) throws IOException {
        InputStream ins;
        int code = conn.getResponseCode();
        if (code >= 400) {
            ins = conn.getErrorStream();
        } else {
            return null;
        }

        // 读取返回数据。
        StringBuilder buf = new StringBuilder();
        BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
        String line;
        while ((line = reader.readLine()) != null) {
            buf.append(line).append("\n");
        }
        reader.close();
        return buf.toString();
    }

    private HttpURLConnection getHttpConnection(String urls) throws IOException {
        URL url = new URL(urls);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setConnectTimeout(5000);
        conn.setReadTimeout(30000);
        conn.setDoInput(true);
        conn.setDoOutput(true);

        conn.setRequestMethod("POST");
        conn.setRequestProperty("User-Agent", "Mozilla/5.0 ( MacOS )");
        return conn;
    }

}